home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / share / perl5 / Mail / Address.pm next >
Text File  |  2008-04-14  |  7KB  |  273 lines

  1. # Copyrights 1995-2008 by Mark Overmeer <perl@overmeer.net>.
  2. #  For other contributors see ChangeLog.
  3. # See the manual pages for details on the licensing terms.
  4. # Pod stripped from pm file by OODoc 1.04.
  5. package Mail::Address;
  6. use vars '$VERSION';
  7. $VERSION = '2.03';
  8. use strict;
  9.  
  10. use Carp;
  11.  
  12. # use locale;   removed in version 1.78, because it causes taint problems
  13.  
  14. sub Version { our $VERSION }
  15.  
  16.  
  17.  
  18. # given a comment, attempt to extract a person's name
  19. sub _extract_name
  20. {   # This function can be called as method as well
  21.     my $self = @_ && ref $_[0] ? shift : undef;
  22.  
  23.     local $_ = shift
  24.         or return '';
  25.  
  26.     # Using encodings, too hard. See Mail::Message::Field::Full.
  27.     return '' if m/\=\?.*?\?\=/;
  28.  
  29.     # trim whitespace
  30.     s/^\s+//;
  31.     s/\s+$//;
  32.     s/\s+/ /;
  33.  
  34.     # Disregard numeric names (e.g. 123456.1234@compuserve.com)
  35.     return "" if /^[\d ]+$/;
  36.  
  37.     s/^\((.*)\)$/$1/; # remove outermost parenthesis
  38.     s/^"(.*)"$/$1/;   # remove outer quotation marks
  39.     s/\(.*?\)//g;     # remove minimal embedded comments
  40.     s/\\//g;          # remove all escapes
  41.     s/^"(.*)"$/$1/;   # remove internal quotation marks
  42.     s/^([^\s]+) ?, ?(.*)$/$2 $1/; # reverse "Last, First M." if applicable
  43.     s/,.*//;
  44.  
  45.     # Change casing only when the name contains only upper or only
  46.     # lower cased characters.
  47.     unless( m/[A-Z]/ && m/[a-z]/ )
  48.     {   # Set the case of the name to first char upper rest lower
  49.         s/\b(\w+)/\L\u$1/igo;  # Upcase first letter on name
  50.         s/\bMc(\w)/Mc\u$1/igo; # Scottish names such as 'McLeod'
  51.         s/\bo'(\w)/O'\u$1/igo; # Irish names such as 'O'Malley, O'Reilly'
  52.         s/\b(x*(ix)?v*(iv)?i*)\b/\U$1/igo; # Roman numerals, eg 'Level III Support'
  53.     }
  54.  
  55.     # some cleanup
  56.     s/\[[^\]]*\]//g;
  57.     s/(^[\s'"]+|[\s'"]+$)//g;
  58.     s/\s{2,}/ /g;
  59.  
  60.     $_;
  61. }
  62.  
  63. sub _tokenise
  64. {   local $_ = join ',', @_;
  65.     my (@words,$snippet,$field);
  66.  
  67.     s/\A\s+//;
  68.     s/[\r\n]+/ /g;
  69.  
  70.     while ($_ ne '')
  71.     {   $field = '';
  72.         if(s/^\s*\(/(/ )    # (...)
  73.         {   my $depth = 0;
  74.  
  75.      PAREN: while(s/^(\(([^\(\)\\]|\\.)*)//)
  76.             {   $field .= $1;
  77.                 $depth++;
  78.                 while(s/^(([^\(\)\\]|\\.)*\)\s*)//)
  79.                 {   $field .= $1;
  80.                     last PAREN unless --$depth;
  81.                 $field .= $1 if s/^(([^\(\)\\]|\\.)+)//;
  82.                 }
  83.             }
  84.  
  85.             carp "Unmatched () '$field' '$_'"
  86.                 if $depth;
  87.  
  88.             $field =~ s/\s+\Z//;
  89.             push @words, $field;
  90.  
  91.             next;
  92.         }
  93.  
  94.         if( s/^("(?:[^"\\]+|\\.)*")\s*//       # "..."
  95.          || s/^(\[(?:[^\]\\]+|\\.)*\])\s*//    # [...]
  96.          || s/^([^\s()<>\@,;:\\".[\]]+)\s*//
  97.          || s/^([()<>\@,;:\\".[\]])\s*//
  98.           )
  99.         {   push @words, $1;
  100.             next;
  101.         }
  102.  
  103.         croak "Unrecognised line: $_";
  104.     }
  105.  
  106.     push @words, ",";
  107.     \@words;
  108. }
  109.  
  110. sub _find_next
  111. {   my ($idx, $tokens, $len) = @_;
  112.  
  113.     while($idx < $len)
  114.     {   my $c = $tokens->[$idx];
  115.         return $c if $c eq ',' || $c eq ';' || $c eq '<';
  116.         $idx++;
  117.     }
  118.  
  119.     "";
  120. }
  121.  
  122. sub _complete
  123. {   my ($class, $phrase, $address, $comment) = @_;
  124.  
  125.     @$phrase || @$comment || @$address
  126.        or return undef;
  127.  
  128.     my $o = $class->new(join(" ",@$phrase), join("",@$address), join(" ",@$comment));
  129.     @$phrase = @$address = @$comment = ();
  130.     $o;
  131. }
  132.  
  133.  
  134. sub new(@)
  135. {   my $class = shift;
  136.     bless [@_], $class;
  137. }
  138.  
  139.  
  140. sub parse(@)
  141. {   my $class = shift;
  142.     my @line  = grep {defined} @_;
  143.     my $line  = join '', @line;
  144.  
  145.     my (@phrase, @comment, @address, @objs);
  146.     my ($depth, $idx) = (0, 0);
  147.  
  148.     my $tokens  = _tokenise @line;
  149.     my $len     = @$tokens;
  150.     my $next    = _find_next $idx, $tokens, $len;
  151.  
  152.     local $_;
  153.     for(my $idx = 0; $idx < $len; $idx++)
  154.     {   $_ = $tokens->[$idx];
  155.  
  156.         if(substr($_,0,1) eq '(') { push @comment, $_ }
  157.         elsif($_ eq '<')    { $depth++ }
  158.         elsif($_ eq '>')    { $depth-- if $depth }
  159.         elsif($_ eq ',' || $_ eq ';')
  160.         {   warn "Unmatched '<>' in $line" if($depth);
  161.             my $o = $class->_complete(\@phrase, \@address, \@comment);
  162.             push @objs, $o if defined $o;
  163.             $depth = 0;
  164.             $next = _find_next $idx+1, $tokens, $len;
  165.         }
  166.         elsif($depth)       { push @address, $_ }
  167.         elsif($next eq "<") { push @phrase,  $_ }
  168.         elsif( /^[.\@:;]$/ || !@address || $address[-1] =~ /^[.\@:;]$/ )
  169.         {   push @address, $_ }
  170.         else
  171.         {   warn "Unmatched '<>' in $line" if $depth;
  172.             my $o = $class->_complete(\@phrase, \@address, \@comment);
  173.             push @objs, $o if defined $o;
  174.             $depth = 0;
  175.             push @address, $_;
  176.         }
  177.     }
  178.     @objs;
  179. }
  180.  
  181.  
  182. sub phrase  { shift->set_or_get(0, @_) }
  183. sub address { shift->set_or_get(1, @_) }
  184. sub comment { shift->set_or_get(2, @_) }
  185.  
  186. sub set_or_get($)
  187. {   my ($self, $i) = (shift, shift);
  188.     @_ or return $self->[$i];
  189.  
  190.     my $val = $self->[$i];
  191.     $self->[$i] = shift if @_;
  192.     $val;
  193. }
  194.  
  195.  
  196. my $atext = '[\-\w !#$%&\'*+/=?^`{|}~]';
  197. sub format
  198. {   my @addrs;
  199.  
  200.     foreach (@_)
  201.     {   my ($phrase, $email, $comment) = @$_;
  202.         my @addr;
  203.  
  204.         if(defined $phrase && length $phrase)
  205.         {   push @addr
  206.               , $phrase =~ /^(?:\s*$atext\s*)+$/o ? $phrase
  207.               : $phrase =~ /(?<!\\)"/             ? $phrase
  208.               :                                    qq("$phrase");
  209.  
  210.             push @addr, "<$email>"
  211.                 if defined $email && length $email;
  212.         }
  213.         elsif(defined $email && length $email)
  214.         {   push @addr, $email;
  215.         }
  216.  
  217.         if(defined $comment && $comment =~ /\S/)
  218.         {   $comment =~ s/^\s*\(?/(/;
  219.             $comment =~ s/\)?\s*$/)/;
  220.         }
  221.  
  222.         push @addr, $comment
  223.             if defined $comment && length $comment;
  224.  
  225.         push @addrs, join(" ", @addr)
  226.             if @addr;
  227.     }
  228.  
  229.     join ", ", @addrs;
  230. }
  231.  
  232.  
  233. sub name
  234. {   my $self   = shift;
  235.     my $phrase = $self->phrase;
  236.     my $addr   = $self->address;
  237.  
  238.     $phrase    = $self->comment
  239.         unless defined $phrase && length $phrase;
  240.  
  241.     my $name   = $self->_extract_name($phrase);
  242.  
  243.     # first.last@domain address
  244.     if($name eq '' && $addr =~ /([^\%\.\@_]+([\._][^\%\.\@_]+)+)[\@\%]/)
  245.     {   ($name  = $1) =~ s/[\._]+/ /g;
  246.     $name   = _extract_name $name;
  247.     }
  248.  
  249.     if($name eq '' && $addr =~ m#/g=#i)    # X400 style address
  250.     {   my ($f) = $addr =~ m#g=([^/]*)#i;
  251.     my ($l) = $addr =~ m#s=([^/]*)#i;
  252.     $name   = _extract_name "$f $l";
  253.     }
  254.  
  255.     length $name ? $name : undef;
  256. }
  257.  
  258.  
  259. sub host
  260. {   my $addr = shift->address || '';
  261.     my $i    = rindex $addr, '@';
  262.     $i >= 0 ? substr($addr, $i+1) : undef;
  263. }
  264.  
  265.  
  266. sub user
  267. {   my $addr = shift->address || '';
  268.     my $i    = index $addr, '@';
  269.     $i >= 0 ? substr($addr,0,$i) : $addr;
  270. }
  271.  
  272. 1;
  273.